package orbit

import (
	"fmt"
	"math"
	"runtime/debug"

	"gddgame.cc/galaxy/utils"
)

func ReadInt32(stream []byte, begin int, length int) int32 {
	tmp := int32(0)
	i := begin
	multiplier := int32(1)
	max := begin + length - 1
	for i < max {
		tmp += int32(stream[i]&255) * multiplier
		multiplier *= 256
		i++
	}
	tmp += int32(stream[i]&127) * multiplier
	if (stream[max] & 0x80) > 0 {
		tmp *= -1
	}

	return tmp
}

func WriteInt32(stream []byte, begin int, length int, value int32) {
	i := begin
	f := value < 0
	if f {
		value *= -1
	}
	var max = begin + length - 1
	for i < max {
		var b = value % 256
		value = value / 256

		stream[i] = (byte)(b & 0xff)
		i++
	}

	if value > 0 {
		stream[max] = (byte)(value & 0xff)
	} else {
		stream[max] = 0
	}

	if f {
		stream[max] = (byte)(stream[max] | 0x80)
	}
}

func EncodeLocation(x int32, y int32) []byte {
	ax := math.Abs(float64(x))
	ay := math.Abs(float64(y))
	length := 0
	if ax < 127 && ay < 127 {
		length = 1
	} else if ax < 32767 && ay < 32767 {
		length = 2
	} else if ax < 8388607 && ay < 8388607 {
		length = 3
	} else {
		length = 4
	}
	positionByte := make([]byte, length*2)

	WriteInt32(positionByte, 0, length, int32(x))
	WriteInt32(positionByte, length, length, int32(y))
	return positionByte
}

func DecodeLocation(location []byte) (int32, int32) {
	length := len(location) / 2
	return ReadInt32(location, 0, length), ReadInt32(location, length, length)
}

func (location *Location) Replace(x Point, y Point) {
	location.X = x
	location.Y = y
}

func (location *Location) Distance(target *Location) float64 {
	x := target.X - location.X
	y := target.Y - location.Y
	return math.Sqrt(float64(x*x + y*y))
}

func (location *Location) Multiple(point Point) Location {
	//fmt.Println("Multiple", location, point, Location{
	//	X: location.X * (point + 1),
	//	Y: location.Y * (point + 1),
	//})
	return Location{
		X: location.X * (point),
		Y: location.Y * (point),
	}
}

func (block *BlockLocation) Offset(x Point, y Point, b *BlockLocation) {
	b.Center.X = block.Center.X + x*block.Gap.X*Point(2)
	b.Center.Y = block.Center.Y + y*block.Gap.Y*2
	b.Gap = block.Gap
	b.GapWrap = block.GapWrap
}
func (block *BlockLocation) Copy(b *BlockLocation) {
	block.Center = b.Center
	block.Gap = b.Gap
	block.GapWrap = b.GapWrap
}
func (block *BlockLocation) DirectionInline(location *Location) Direction {
	return block.Direction(location, &Location{
		X: block.GapWrap.X - block.Gap.X,
		Y: block.GapWrap.Y - block.Gap.Y,
	})
}
func (block *BlockLocation) Direction(location *Location, vision *Location) Direction {
	direction := NoneDirection
	eX := block.Gap.X - vision.X
	if block.Center.X-eX >= location.X {
		direction |= LeftDirection
	} else if block.Center.X+eX <= location.X {
		direction |= RightDirection
	}
	eY := block.Gap.Y - vision.Y
	if block.Center.Y-eY >= location.Y {
		direction |= BottomDirection
	} else if block.Center.Y+eY <= location.Y {
		direction |= TopDirection
	}
	return direction
}

func (block *BlockLocation) Contain(location *Location) bool {
	if block.GapWrap.X == 0 && block.Gap.X == 0 {
		return true
	}
	flag := true
	// 范围内的为正
	if block.Center.X-block.GapWrap.X > location.X {
		flag = false
	} else if block.Center.X+block.GapWrap.X <= location.X {
		flag = false
	}
	// 范围外的为反
	if block.Center.Y-block.GapWrap.Y > location.Y {
		flag = false
	} else if block.Center.Y+block.GapWrap.Y <= location.Y {
		flag = false
	}
	return flag
}

func (block *BlockLocation) Index(location *Location) int {
	gapX := block.Gap.X * 2
	index := 0
	if block.Center.X > 0 {
		index = int((location.X - (block.Center.X - block.Gap.X)) * gapX)
	} else {
		index = int(((block.Center.X + block.Gap.X) - location.X) * gapX)
	}
	if block.Center.Y > 0 {
		index += int(location.Y - (block.Center.Y - block.Gap.Y))
	} else {
		index += int((block.Center.Y + block.Gap.Y) - location.Y)
	}
	return index
}

func (block *BlockLocation) FromIndex(index int, location *Location) {
	gapX := block.Gap.X * 2
	gapY := block.Gap.Y * 2
	location.X = block.Center.X + Point((index-index%int(gapX))/int(gapX))
	location.Y = block.Center.Y + Point(index%int(gapY))
}

func (block *BlockLocation) ToNode() string {
	b, _ := block.MarshalMsg(nil)
	return utils.ToString(b)
}

func (block *BlockLocation) FromNode(node string) {
	bytes := utils.ToBytes(node)
	_, err := block.UnmarshalMsg(bytes)
	if err != nil {
		debug.PrintStack()
		fmt.Println("block from node", err)
	}
}

func (block *BlockLocation) Size() int {
	return int(block.Gap.X) * int(block.Gap.Y) * 4
}
